# Untitled - By: JYL - 周六 6月 22 2024
###################################################### boot.py
import KPU as kpu
import gc, sys
from machine import UART, Timer
from fpioa_manager import fm
import sensor, image, lcd, time, utime

lcd.init(type=1,freq=15000000,color=lcd.BLACK)  # 初始化LCD.

sensor.reset()                          # 复位和初始化摄像头,执行sensor.run(0)停止
sensor.set_pixformat(sensor.RGB565)     # 每像素为16位(两字节),5位用于红色,6位用于绿色,5位用于蓝色,处理速度比灰度图像要慢
sensor.set_framesize(sensor.QVGA)       # 设置每帧大小(即图像尺寸=320*240)
sensor.set_windowing((224, 224))
sensor.set_hmirror(True)
sensor.set_vflip(True)                  # 将摄像头设置成后置模式（所见即所得）
sensor.skip_frames(time = 2000)         # 摄像头配置后跳过n帧或者等待时间time让其变的稳定.
sensor.run(1)

fm.register(11, fm.fpioa.UART1_TX, force=True)      # 25
fm.register(9,  fm.fpioa.UART1_RX, force=True)      # 24
uart1 = UART(UART.UART1,115200,8,1,0,timeout=1000, read_buf_len=4096)

clock = time.clock()                    # 创建一个时钟来追踪FPS(每秒拍摄帧数)


###################################################### main.py
red_threshold = (0,100,20,127,-5,127)    # 颜色的阈值需要设置成全局变量，而不应该包含在类中，特别是多个类都在使用这个颜色阈值，更换环境时需要重新调整阈值

# 定义颜色特征类，主要用于保存找到的色块的一些信息，以及色块的阈值，寻找区域等信息
class color_feature():
    cx                =  0              # 色块 x轴 中心坐标
    cy                =  0              # 色块 y轴 中心坐标
    cw                =  0              # 色块宽度
    density           =  0              # 色块密度比 反映色块锁定程度 值越大 锁定程度越好
    pixels_max        =  0              # 色块像素最大值
    find_flag         =  0              # 识别到标志位，（没有识别到串口不发送）

    color_threshold   = red_threshold   # 设置颜色的阈值，可以在阈值编辑器得到
    roi               = (0,0,224,224)   # 色块寻找区域（感兴趣区域：x,y,w,h）
    x_stride          =  10             # 色块 x轴 像素最小宽度 色块如果比较大可以调大此参数 提高寻找速度
    y_stride          =  10             # 色块 y轴 像素最小宽度 色块如果比较大可以调大此参数 提高寻找速度
    pixels_threshold  =  100            # 色块 像素个数阈值 例如调节此参数为100 则可以滤除色块像素小于100的色块
    area_threshold    =  100            # 色块 被框面积阈值 例如调节此参数为100 则可以滤除色块被框面积小于100的色块
    merge             =  True           # 是否合并寻找到的色块 True 则合并 False 则不合并
    margin            =  1              # 色块合并间距 例如调节此参数为1 若上面选择True合并色块 且被找到的色块有多个 相距1像素 则会将这些色块合并

# 实例化类时，若没有声明其成员，则会继承默认参数
Line = color_feature()                  # 实例化类：红色巡线
Line.roi              = (100,62,16,100) # 巡线的感兴趣区（横向兴趣小、竖向兴趣大、近端：转向前仍需巡线）
Line.area_threshold   =  200                    # 线的被框选区域需要比较大才行？
Line.margin           =  1

Cross = color_feature()                 # 实例化类：红色十字路口
Cross.roi             = (180,0,8,224)   # 路口的感兴趣区（远端、横向兴趣大、竖向兴趣小）
Cross.area_threshold  =  100            #
Cross.margin          =  120                    # 这个间距合并是较大还是较小？

# 定义寻找色块函数
def opv_find_blobs(img,color):
    color.pixels_max  = 0               # 重置 色块 最大像素数量
    color.find_flag   = 0               # 重置 找到 标志位

    for blobs in img.find_blobs([color.color_threshold],roi=color.roi,
    x_stride=color.x_stride,y_stride=color.y_stride,
    pixels_threshold=color.pixels_threshold,area_threshold=color.area_threshold,
    merge=color.merge,margin=color.margin):
        img.draw_rectangle(blobs[0:4])                      # 圈出找到的色块
        if color.pixels_max < blobs.pixels():               # 找到面积最大的色块
            color.pixels_max = blobs.pixels()
            color.cx = blobs.cx()                           # 将面积最大的色块的 x轴 中心坐标值 赋值给 color
            color.cy = blobs.cy()                           # 将面积最大的色块的 y轴 中心坐标值 赋值给 color
            color.cw = blobs[3]
            color.density = blobs.density()                 # 将面积最大的色块的 色块密度比 赋值给 color
            color.find_flag = 1                             # 标志画面中有找到色块

    if color.find_flag == 1:                                # 标记画面中被找到的最大色块的中心坐标
        img.draw_cross(color.cx,color.cy, color=127, size = 15)
        img.draw_circle(color.cx,color.cy, 15, color = 127)

# 定义打印色块参数函数
def print_blobs_property(color,name):
    print(name,"cx:",color.cx,"cy:",color.cy,"flag:",color.flag,"color:",color.color,"density:",color.density,"led_flag:",color.led_flag)

# 定义串口/LCD发送函数
def sending_data(img,class1,class2,fps):
    ch = 'O'
    if class2.find_flag==1 and class2.cw>50 :ch='T'
    if class2.find_flag==0 and class1.find_flag==0 :ch='N'
    str = '[%c,%03d]'%(ch,class1.cy)    #'\r\n'
    #str = "({},{})\r\n".format(ch,class1.cy)
    print(str)   # 这个是调试信息打印，一般是USB打印出来的
    uart1.write(str) # 硬件上的串口发送
    img.draw_string(0,0,str)

#定时器回调函数
def fun(tim):
    sending_data(img,Line,Cross,clock.fps());

#定时器 0 初始化，周期 1 秒
tim = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PERIODIC,period=100, callback=fun)


while(True):
    clock.tick()                        # 更新FPS帧数
    img = sensor.snapshot()             # 拍摄一个图片并保存
    opv_find_blobs(img,Line)            # 检测图片中的巡线
    opv_find_blobs(img,Cross)           # 检测图片中的路口
    lcd.display(img)                    # 在LCD上显示




